#include <rtthread.h>
#include <stdint.h>
#include <stdbool.h>
#include "huart.h"
#include "mfifoblock.h"
#include "usbd_cdc_uart.h"

#include "DAP_config.h"
#include "dap_if_config.h"

#include "usb_util.h"

#include "usb_cdc.h"
#include "usbd_cdc.h"
#include "usbd_core.h"

#ifndef UART_BUFFER_SIZE
#define UART_BUFFER_SIZE CDC_PACKET_SIZE
#endif
#define UART_RECV_BUFFER_NUM 4

static struct cdc_line_coding LINE_CODING = {
    .dwDTERate = CONFIG_CDC_DEFAULT_BAUDRATE,
    .bDataBits = CONFIG_CDC_DEFAULT_DATABIT,
    .bParityType = CONFIG_CDC_DEFAULT_PARITYTYPE,
    .bCharFormat = CONFIG_CDC_DEFAULT_CHARFORMAT,
};

static uint8_t BULK_IN_IDLE_FLAG = 1;

static struct mfifoblock MFB_UART_RECV_BUFFER;

rt_align((RT_ALIGN_SIZE)) static uint8_t __UART_SEND_BUFFER[UART_BUFFER_SIZE] = {0};
static uint8_t *UART_SEND_BUFFER = __UART_SEND_BUFFER;

rt_align((RT_ALIGN_SIZE)) static uint8_t __UART_RECV_BUFFER[UART_RECV_BUFFER_NUM * UART_BUFFER_SIZE] = {0};
static mblock_t UART_RECV_BLOCK[UART_RECV_BUFFER_NUM] = {0};

static int usbd_cdc_uart_buffer_init(void)
{
    rt_kprintf("MFB_UART_RECV_BUFFER: %p\n", &MFB_UART_RECV_BUFFER);
    mfifoblock_map(UART_RECV_BLOCK, (uint8_t *)__UART_RECV_BUFFER, UART_RECV_BUFFER_NUM, UART_BUFFER_SIZE);
    mfifoblock_init(&MFB_UART_RECV_BUFFER, UART_RECV_BLOCK, UART_RECV_BUFFER_NUM, UART_BUFFER_SIZE);

    return 0;
}
INIT_PREV_EXPORT(usbd_cdc_uart_buffer_init);

void usbd_cdc_event_configure(void)
{
    static uint8_t *uart_recv_buf = NULL;

    BULK_IN_IDLE_FLAG = 1;

    uart_init();

    mfifoblock_reset(&MFB_UART_RECV_BUFFER);

    usbd_ep_start_read(CDC_OUT_EP, UART_SEND_BUFFER, CDC_PACKET_SIZE);

    mfifoblock_write_block_get(&MFB_UART_RECV_BUFFER, &uart_recv_buf);
    uart_recv(uart_recv_buf, UART_BUFFER_SIZE);
}

void usbd_cdc_event_disconnected(void)
{
    uart_deinit();
}

void uart_send_callback(uint8_t *data, uint16_t len)
{
    usbd_ep_start_read(CDC_OUT_EP, UART_SEND_BUFFER, CDC_PACKET_SIZE);
}

void _usbd_cdc_bulk_in_deal(void)
{
    static uint8_t *uart_recv_buf = NULL;
    static uint8_t *usbd_cdc_in_buf = NULL;
    static rt_uint16_t usbd_cdc_in_buf_len = 0;

    if (mfifoblock_write_block_get(&MFB_UART_RECV_BUFFER, &uart_recv_buf) == 0)
    {
        uart_recv(uart_recv_buf, UART_BUFFER_SIZE);
    }

    if (mfifoblock_read_block_get(&MFB_UART_RECV_BUFFER, &usbd_cdc_in_buf, &usbd_cdc_in_buf_len) == 0)
    {
        if (BULK_IN_IDLE_FLAG)
        {
            usbd_ep_start_write(CDC_IN_EP, usbd_cdc_in_buf, usbd_cdc_in_buf_len);
            BULK_IN_IDLE_FLAG = 0;
        }
    }
}

void uart_recv_callback(uint8_t *data, uint16_t len)
{
    mfifoblock_write_block_increase(&MFB_UART_RECV_BUFFER, len);

    _usbd_cdc_bulk_in_deal();
}

void usbd_cdc_acm_bulk_out(uint8_t ep, rt_uint32_t nbytes)
{
    uart_send(UART_SEND_BUFFER, nbytes);
}

void usbd_cdc_acm_bulk_in(uint8_t ep, rt_uint32_t nbytes)
{
    if ((nbytes % CDC_PACKET_SIZE) == 0 && nbytes)
    {
        /* send zlp */
        usbd_ep_start_write(CDC_IN_EP, NULL, 0);
        return;
    }
    BULK_IN_IDLE_FLAG = 1;
    mfifoblock_read_block_increase(&MFB_UART_RECV_BUFFER);
    _usbd_cdc_bulk_in_deal();
}

void usbd_cdc_acm_set_line_coding(uint8_t intf, struct cdc_line_coding *line_coding)
{
    memcpy(&LINE_CODING, line_coding, sizeof(struct cdc_line_coding));
    uart_configure(line_coding->dwDTERate, line_coding->bCharFormat, line_coding->bParityType, line_coding->bDataBits);
    // rt_kprintf("line_coding: %d, %d, %d, %d  timeout_ticks: %d\n", HUART_LL.line_coding.dwDTERate,
    //            HUART_LL.line_coding.bDataBits, HUART_LL.line_coding.bParityType, HUART_LL.line_coding.bCharFormat,
    //            HUART_LL.recv_timeout_ticks);
}

void usbd_cdc_acm_get_line_coding(uint8_t intf, struct cdc_line_coding *line_coding)
{
    memcpy(line_coding, &LINE_CODING, sizeof(struct cdc_line_coding));
}

void usbd_cdc_acm_set_dtr(uint8_t intf, bool dtr)
{
    // rt_kprintf("usbd_cdc_acm_set_dtr  intf: %d, dtr: %d\n", intf, dtr);
}

void usbd_cdc_acm_set_rts(uint8_t intf, bool rts)
{
    // rt_kprintf("usbd_cdc_acm_set_rts  intf: %d, rts: %d\n", intf, rts);
}

void usbd_cdc_acm_send_break(uint8_t intf)
{
    // rt_kprintf("usbd_cdc_acm_send_break  intf: %d\n", intf);
}
